:- module(xml).
:- export xml_pp / 1.
:- export xml_subterm / 2.
:- export xml_parse / 3.
:- export xml_parse / 2.
:- comment(summary, 'A bi-directional XML parser').
:- comment(author, "John Fletcher, ECLiPSe wrapper by Joachim Schimpf").
:- comment(copyright, "Copyright (C) 2001, 2002 Binding Time Limited").
:- comment(desc, html("\r\n<h2>Note for ECLiPSe users</h2>\r\n<p>\r\nThis code creates and accepts character lists rather than ECLiPSe strings. \r\nTo convert between character lists and (UTF8 or ASCII) strings, use the\r\nECLiPSe built-in string_list/3. For example, to parse a UTF-8 encoded\r\nXML file, use the following code:\r\n</p><pre>\r\nxml_parse_file(File, Document) :-\r\n\topen(File, read, Stream),\r\n\tread_string(Stream, end_of_file, _, Utf8String),\r\n\tclose(Stream),\r\n\tstring_list(Utf8String, Chars, utf8),\r\n\txml_parse(Chars, Document).\r\n</pre><p>\r\nMost of the subsequent text is taken literally from\r\n<a href=\"http://www.john.fletcher.dial.pipex.com/xml.pl.shtml\">\r\nhttp://www.john.fletcher.dial.pipex.com/xml.pl.shtml</a>.\r\n</p>\r\n\r\n\r\n<h2>TERMS AND CONDITIONS</h2>\r\n<p>\r\nThis program is offered free of charge, as unsupported source code. You may\r\nuse it, copy it, distribute it, modify it or sell it without restriction. \r\n</p><p>\r\nWe hope that it will be useful to you, but it is provided \"as is\" without\r\nany warranty express or implied, including but not limited to the warranty\r\nof non-infringement and the implied warranties of merchantability and fitness\r\nfor a particular purpose.\r\n</p><p>\r\nBinding Time Limited will not be liable for any damages suffered by you as\r\na result of using the Program. In no event will Binding Time Limited be\r\nliable for any special, indirect or consequential damages or lost profits\r\neven if Binding Time Limited has been advised of the possibility of their\r\noccurrence. Binding Time Limited will not be liable for any third party\r\nclaims against you.\r\n</p>\r\n<pre>\r\nHistory:\r\n$Log: xml_comments.ecl,v $\nRevision 1.1  2003/03/31 13:58:02  js10\nUpgraded to latest version from John Fletcher's web site\n\r\nRevision 1.2  2002/03/26 22:56:55  js10\r\nAdded John Fletcher's public domain XML parser/generator\r\n\r\nRevision 1.1  2002/03/26 22:50:07  js10\r\nAdded John Fletcher's public domain XML parser/generator\r\n\r\nRevision 1.1  2002-01-31 21:04:45+00  john\r\nUpdated Copyright statements.\r\n\r\nRevision 1.0  2001-10-17 20:46:24+01  john\r\nInitial revision\r\n</pre>\r\n\r\n  <h2>Background</h2>\r\n  <p>xml.pl is a module for parsing <acronym title=\"eXtensible Markup Language\">\r\nXML</acronym> with Prolog, which provides Prolog applications with a simple\r\ninterface to XML documents. We have used it successfully in a number of\r\napplications.</p>\r\n  <p>It supports a subset of XML suitable for XML Data and Worldwide Web\r\n  applications. It is neither as strict nor as comprehensive as the <a href=\"http://www.w3.org/TR/2000/REC-xml-20001006\">XML 1.0 Specification</a>\r\nmandates.</p>\r\n  <p>It is not as strict, because, while the specification must eliminate\r\nambiguities, not all errors need to be regarded as faults, and some reasonable\r\nexamples of real XML usage would have to be rejected if they were.</p>\r\n  <p>It is not as comprehensive, because, where the XML specification makes\r\n  provision for more or less complete <acronym title=\"Document Type Declaration\">DTD</acronym>s to be provided as part of a\r\ndocument, xml.pl supports the local definition of ENTITIES only.</p>\r\n  <p>We have placed <a href=\"xml_download.shtml\">the code, and a small Windows\r\napplication which embodies it</a>, into the public domain, to encourage the use\r\nof Prolog with XML.</p>\r\n  <p>We hope that they will be useful to you, but they are not supported, and\r\nBinding Time Limited accept NO LIABILITY WHATSOEVER in respect of their\r\nuse.</p>\r\n  <h2>Specification</h2>\r\n  <p>Three predicates are exported by the module: xml_parse/[2,3], xml_subterm/2\r\nand xml_pp/1.</p>\r\n  <p><dfn>xml_parse( {+Controls}, +?Chars, ?+Document )</dfn> parses <var>\r\nChars</var>, a list of character codes, to/from a data structure of the form\r\n<code class=\"Contrast\">\r\nxml(&lt;attributes&gt;, &lt;content&gt;)\r\n</code> , where:</p>\r\n  <p><code class=\"Contrast\">&lt;attributes&gt;</code> is a list of <code class=\"Contrast\">\r\n &lt;name&gt;=&lt;data&gt;\r\n</code> attributes from the (possibly implicit) XML signature of the\r\ndocument.</p>\r\n  <p><code class=\"Contrast\">&lt;content&gt;</code> is a (possibly empty) list comprising occurrences of :</p>\r\n  <table>\r\n   <tr>\r\n    <td><code class=\"Contrast\">pcdata(&lt;data&gt;)</code></td>\r\n    <td>Text</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\"> comment(&lt;string&gt;)</code></td>\r\n    <td>An xml comment;</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">namespace(&lt;URI&gt;,&lt;prefix&gt;,&lt;element&gt;)</code></td>\r\n    <td>a Namespace</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\"> element(&lt;tag&gt;, &lt;attributes&gt;, &lt;content&gt;)</code></td>\r\n    <td>&lt;tag&gt;..&lt;/tag&gt; encloses &lt;content&gt; or &lt;tag /&gt; if empty</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\"> instructions(&lt;name&gt;, &lt;data&gt;)</code></td>\r\n    <td> A PI  &lt;? &lt;name&gt; &lt;data&gt; ?&gt;</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\"> cdata(&lt;data&gt;)</code></td>\r\n    <td>&lt;![CDATA[ &lt;string&gt; ]]&gt;</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\"> doctype(&lt;tag&gt;, &lt;doctype id&gt;)</code></td>\r\n    <td> DTD &lt;!DOCTYPE .. &gt;</td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\">&#160;</td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\"><p>The conversions are not completely symmetrical, in that weaker XML is\r\naccepted than can be generated. Specifically, in-bound <em>(Chars -&gt;\r\nDocument)</em> parsing does not require strictly well-formed XML. If <var>\r\nChars</var> does not represent well-formed XML, <var>Document</var> is\r\ninstantiated to the term <code class=\"Contrast\">\r\nmalformed(&lt;attributes&gt;, &lt;content&gt;)\r\n</code> .</p></td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\">&#160;</td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\"><p>The <code class=\"Contrast\">&lt;content&gt;</code> of a <code>malformed/2</code> structure can include:</p></td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\">&#160;</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">unparsed( &lt;string&gt; )</code></td>\r\n    <td>Text which has not been parsed</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">out_of_context( &lt;tag&gt; )</code></td>\r\n    <td>&lt;tag&gt; is not closed</td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\"><p>in addition to the parsed term types.</p><p>Out-bound <em>(Document -&gt; Chars)</em> parsing <em>does</em> require that\r\n<var>Document</var> defines well-formed XML. If an error is detected a &#39;domain&#39;\r\nexception is raised.</p><p>The domain exception will attempt to identify the particular sub-term in\r\nerror and the message will show a list of its ancestor elements in the form\r\n<code>&lt;tag&gt;{(id)}*</code> where <code>\r\n&lt;id&gt;\r\n</code> is the value of any attribute <em>named</em> id.</p><p>At this release, the Controls applying to in-bound <em>(Chars -&gt;\r\nDocument)</em> parsing are:</p></td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\">&#160;</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">extended_characters(&lt;bool&gt;)</code></td>\r\n    <td>Use the extended character entities for XHTML (default true)</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\"> format(&lt;bool&gt;)</code></td>\r\n    <td>Strip layouts when no non-layout character data appears between elements. default\r\ntrue)</td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\"><p>For out-bound <em>(Document -&gt; Chars)</em> parsing, the only available\r\noption is:</p></td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\">&#160;</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">format(&lt;bool&gt;)</code></td>\r\n    <td>Indent the element content, (default true)</td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\">&#160;</td>\r\n   </tr>\r\n   <tr>\r\n    <td colspan=\"2\"><h4>Types</h4></td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">&lt;tag&gt;</code></td>\r\n    <td>An atom naming an element</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">&lt;data&gt;</code></td>\r\n    <td>A &quot;string&quot;</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">&lt;name&gt;</code></td>\r\n    <td>An atom, not naming an element</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">&lt;URI&gt;</code></td>\r\n    <td>An atom giving the URI of a Namespace</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">&lt;string&gt;</code></td>\r\n    <td>A &quot;string&quot;: list of character codes.</td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">&lt;doctype id&gt;</code></td>\r\n    <td>one of <code class=\"SourceCode\">public(&lt;string&gt;, &lt;string&gt;)</code>, <code class=\"SourceCode\">system(&lt;string&gt;)</code> or <code class=\"SourceCode\">local</code></td>\r\n   </tr>\r\n   <tr>\r\n    <td><code class=\"Contrast\">&lt;bool&gt;</code></td>\r\n    <td>one of &#39;true&#39; or &#39;false&#39;</td>\r\n   </tr>\r\n  </table>\r\n  <p><dfn>xml_subterm( +XMLTerm, ?Subterm )</dfn> unifies <var>Subterm</var> with\r\na sub-term of <var>Term</var>. This can be especially useful when trying to\r\ntest or retrieve a deeply-nested subterm from a document. Note that <var>\r\nXMLTerm</var> is a sub-term of itself.</p>\r\n  <p><dfn>xml_pp( +XMLDocument )</dfn>&quot;pretty prints&quot; <var>XMLDocument</var> on\r\nthe current output stream.</p>\r\n  <h2>Features of xml.pl</h2>\r\n  <p>The xml/2 data structure has some useful properties.</p>\r\n  <h3>Reusability</h3>\r\n  <p>Using an &quot;abstract&quot; Prolog representation of XML, in which terms represent\r\ndocument &quot;nodes&quot;, makes the parser reuseable for any XML application.</p>\r\n  <p>In effect, xml.pl encapsulates the application-independent tasks of document\r\nparsing and generation, which is essential where documents have components\r\nfrom more than one Namespace.</p>\r\n  <h3>Same Structure</h3>\r\n  <p>The Prolog term representing a document has the same structure as the\r\ndocument itself, which makes the correspondence between the literal\r\nrepresentation of the Prolog term and the XML source readily apparent.</p>\r\n  <p>For example, this simple <a href=\"http://www.w3.org/Graphics/SVG/Overview.htm8\"><acronym title=\"Scalable Vector Graphics\">SVG</acronym></a> image:</p>\r\n  <pre class=\"Contrast\">&lt;?xml version=&quot;1.0&quot; standalone=&quot;no&quot;?&gt;\r\n&lt;!DOCTYPE svg PUBLIC &quot;-//W3C//DTD SVG 1.0//EN&quot; &quot;http://www.w3.org/.../svg10.dtd&quot;\r\n    [\r\n    &lt;!ENTITY redblue &quot;fill: red; stroke: blue; stroke-width: 1&quot;&gt;\r\n    ]&gt;\r\n&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;500&quot; height=&quot;500&quot;&gt;\r\n &lt;circle cx=&quot; 25 &quot; cy=&quot; 25 &quot; r=&quot; 24 &quot; style=&quot;&amp;redblue;&quot;/&gt;\r\n&lt;/svg&gt;</pre>\r\n  <p>... translates into this Prolog term:</p>\r\n  <pre class=\"SourceCode\">xml( [version=&quot;1.0&quot;, standalone=&quot;no&quot;],\r\n    [\r\n    doctype( svg, public( &quot;-//W3C//DTD SVG 1.0//EN&quot;, &quot;http://www.w3.org/.../svg10.dtd&quot; ) ),\r\n    namespace( &#39;http://www.w3.org/2000/svg&#39;, &quot;&quot;,\r\n        element( svg,\r\n            [width=&quot;500&quot;, height=&quot;500&quot;],\r\n            [\r\n            element( circle,\r\n                [cx=&quot;25&quot;, cy=&quot;25&quot;, r=&quot;24&quot;, style=&quot;fill: red; stroke: blue; stroke-width: 1&quot;],\r\n                [] )\r\n            ] )\r\n        )\r\n    ] ).</pre>\r\n  <h3>Efficient Manipulation</h3>\r\n  <p>Each type of node in an XML document is represented by a different Prolog\r\nfunctor, while data, (PCDATA, CDATA and Attribute Values), are left as\r\n&quot;strings&quot;, (lists of character codes).</p>\r\n  <p>The use of distinct functors for mark-up structures enables the efficient\r\nrecursive traversal of a document, while leaving the data as strings\r\nfacilitates application specific parsing of data content (aka <a href=\"http://www.google.com/search?q=%22Micro-parsing%22+XML\">\r\nMicro-parsing</a>).</p>\r\n  <p>For example, to turn every CDATA node into a PCDATA node with tabs expanded\r\ninto spaces:</p>\r\n  <pre class=\"SourceCode\">\r\ncdata_to_pcdata( cdata(CharsWithTabs), pcdata(CharsWithSpaces) ) :-\r\n    tab_expansion( CharsWithTabs, CharsWithSpaces ).\r\ncdata_to_pcdata( xml(Attributes, Content1), xml(Attributes, Content2) ) :-\r\n    cdata_to_pcdata( Content1, Content2 ).\r\ncdata_to_pcdata( namespace(URI,Prefix,Content1), namespace(URI,Prefix,Content2) ) :-\r\n    cdata_to_pcdata( Content1, Content2 ).\r\ncdata_to_pcdata( element(Name,Attrs,Content1), element(Name,Attrs,Content2) ) :-\r\n    cdata_to_pcdata( Content1, Content2 ).\r\ncdata_to_pcdata( [], [] ).\r\ncdata_to_pcdata( [H1|T1], [H2|T2] ) :-\r\n    cdata_to_pcdata( H1, H2 ),\r\n    cdata_to_pcdata( T1, T2 ).\r\ncdata_to_pcdata( pcdata(Chars), pcdata(Chars) ).\r\ncdata_to_pcdata( comment(Chars), comment(Chars) ).\r\ncdata_to_pcdata( instructions(Name, Chars), instructions(Name, Chars) ).\r\ncdata_to_pcdata( doctype(Tag, DoctypeId), doctype(Tag, DoctypeId) ).\r\n</pre>\r\n  <p>The above uses no &#39;cuts&#39;, but will not create any choice points with ground\r\ninput.</p>\r\n  <h3>Elegance</h3>\r\n  <p>The resolution of entity references and the decomposition of the document\r\ninto distinct nodes means that the calling application is not concerned with\r\nthe occasionally messy syntax of XML documents.</p>\r\n  <p>For example, the clean separation of namespace nodes means that Namespaces,\r\nwhich are useful in combining specifications developed separately, have similar\r\nusefulness in combining applications developed separately.</p>\r\n  <p><a href=\"xml_download.shtml\">The source code is available here</a>. Although\r\nit is unsupported, please feel free to <a href=\"mailto:xml@binding-time.co.uk\">\r\ne-mail queries and suggestions</a>. We will respond as time allows.</p>\r\n")).
:- comment(xml_parse / 2, [summary : "Parse or generate XML documents", amode : xml_parse(+, -), amode : xml_parse(-, +), args : ["Chars" : "List of characters", "Document" : "Document as structured term"], see_also : [xml_parse / 3, xml_subterm / 2, xml_pp / 1]]).
:- comment(xml_parse / 3, [summary : "Parse or generate XML documents", amode : xml_parse(+, +, -), amode : xml_parse(+, -, +), args : ["Controls" : "List of options", "Chars" : "List of characters (XML text)", "Document" : "Document as structured term"], see_also : [xml_parse / 2], desc : ascii("\r\nxml_parse( {+Controls}, +?Chars, ?+Document ) parses Chars to/from a data\r\nstructure of the form xml(<atts>, <content>). <atts> is a list of\r\n<atom>=<string> attributes from the (possibly implicit) XML signature of the\r\ndocument. <content> is a (possibly empty) list comprising occurrences of :\r\n\r\npcdata(<string>)\t\t:\tText\r\ncomment(<string>)\t\t:\tAn xml comment;\r\nelement(<tag>,<atts>,<content>)\t:\t<tag>..</tag> encloses <content>\r\n\t\t\t\t:       <tag /> if empty\r\ninstructions(<atom>, <string>)\t:\tProcessing <? <atom> <params> ?>\r\ncdata( <string> )\t\t:\t<![CDATA[ <string> ]]>\r\ndoctype(<atom>, <doctype id>)\t:\tDTD <!DOCTYPE .. >\r\n\r\nThe conversions are not completely symmetrical, in that weaker XML is\r\naccepted than can be generated. Specifically, in-bound (Chars -> Document)\r\ndoes not  require strictly well-formed XML. Document is instantiated to the\r\nterm malformed(Attributes, Content) if Chars does not represent well-formed\r\nXML. The Content of a malformed/2 structure can contain:\r\n\r\nunparsed( <string> )\t\t:\tText which has not been parsed\r\nout_of_context( <tag> )\t\t:\t<tag> is not closed\r\n\r\nin addition to the standard term types.\r\n\r\nOut-bound (Document -> Chars) parsing _does_ require that Document defines\r\nstrictly well-formed XML. If an error is detected a 'domain' exception is\r\nraised.\r\n\r\nThe domain exception will attempt to identify the particular sub-term in\r\nerror and the message will show a list of its ancestor elements in the form\r\n<tag>{(id)}* where <id> is the value of any attribute _named_ id.\r\n\r\nAt this release, the Controls applying to in-bound (Chars -> Document)\r\nparsing are:\r\n\r\nextended_characters(<bool>)\t:\tUse the extended character\r\n\t\t\t\t:\tentities for XHTML (default true)\r\n\r\nformat(<bool>)\t\t\t:\tStrip layouts when no character data\r\n\t\t\t\t:\tappears between elements.\r\n\t\t\t\t:\t(default true)\r\n\r\n[<bool> is one of 'true' or 'false']\r\n\r\nFor out-bound (Document -> Chars) parsing, the only available option is:\r\n\r\nformat(<Bool>)\t\t\t:\tIndent the element content\r\n\t\t\t\t:\t(default true)\r\n\r\nDifferent DCGs for input and output are used because input parsing is\r\nmore flexible than output parsing. Errors in input are recorded as part\r\nof the data structure. Output parsing throws an exception if the document\r\nis not well-formed, diagnosis tries to identify the specific culprit term.\r\n")]).
:- comment(xml_pp / 1, [summary : "Pretty-prints XMLDocument on the current output stream", amode : xml_pp(+), args : ["XMLDocument" : "Document as structured term"], see_also : [xml_parse / 2, xml_parse / 3, xml_subterm / 2]]).
:- comment(xml_subterm / 2, [summary : "Unifies Subterm with a sub-term of Term.", amode : xml_subterm(+, ?), args : ["XMLTerm" : "Structured term", "Subterm" : "Structured term"], see_also : [xml_parse / 2, xml_parse / 3, xml_pp / 1]]).
